home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 14 / CU Amiga Magazine's Super CD-ROM 14 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-09].iso / CUCD / Programming / SAS-C / sc655pch / guiprof / report.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-26  |  37.3 KB  |  1,125 lines

  1. /*-------------------------------------------------------------------*/
  2. /* Copyright (c) 1992-1995 SAS Institute Inc., Cary NC               */
  3. /*                                                                   */
  4. /* AUTHORS:    Doug Walker and Lisa Dixon                            */
  5. /*                                                                     */
  6. /* 1. The external array GPInfo contains all the information needed    */
  7. /*    to put up the histogram.  GPCur is the number of elements in     */
  8. /*    the GPInfo array.  Do not modify GPInfo or GPCur.                */
  9. /*    Look in the file guiprofpriv.h for the definition of the         */
  10. /*    structure pointed to by GPInfo.                                  */
  11. /*                                                                     */
  12. /* 2. The external variable report_type tells you what kind of report  */
  13. /*    to produce:                                                      */
  14. /*       Percentage - percentage of total time                         */
  15. /*       Count      - Count of number of calls                         */
  16. /*       Time(incl) - Time in function including subroutines           */
  17. /*       Time(excl) - Time in function excluding subroutines           */
  18. /*                                                                     */
  19. /* 4. The function InitReport will be run at GUIPROF startup.          */
  20. /*    _STDReport will run after it exits.  Use these functions for     */
  21. /*    initialization and cleanup.                                      */
  22. /*-------------------------------------------------------------------*/
  23.  
  24. #include <assert.h>
  25. #include <string.h>
  26. #include <ctype.h>
  27. #include <proto/exec.h>
  28. #include <proto/dos.h>
  29. #include <proto/gadtools.h>
  30. #include <proto/intuition.h>
  31. #include <proto/graphics.h>
  32. #include "guiprofpriv.h"
  33. #include "SMBar.h" /* Controls the scroll bar */
  34.  
  35. #define GADP(x) ((struct Gadget *)(x))
  36.  
  37. /* These two defines calculate the position and size of the scroll bar */
  38. /* based on the external variables GPCur and NumOnScreen.  GPCur is the*/
  39. /* total number of functions we have data on, and NumOnScreen is the   */
  40. /* number that will fit on the screen at once.  VBMAXPOS, defined in   */
  41. /* "smbar.h", is the maximum range of the scroll bar.                  */
  42. #define VBPOS(x) (GPCur-NumOnScreen ? (ULONG)VBMAXPOS*(x)/(GPCur-NumOnScreen) : 0)
  43. #define VBSIZ(x) (GPCur ? VBMAXPOS*(x)/GPCur : VBMAXPOS)
  44.  
  45. #if 0
  46. #define RBUG(x) printf x ;
  47. #else
  48. #define RBUG(x) ;
  49. #endif
  50.  
  51. static void KillCaches(void); /* Forces a complete redraw */
  52. static void SetNums(void);    /* Calculates range and sets up NumOnScreen */
  53. static void InstallMenus(struct VisualInfo *vi); /* install menus */
  54.  
  55. /* report_type contains a RPT_ value as defined in guiprofpriv.h */
  56. /* oldreport_type contains the value of report_type as of the  */
  57. /* last time Report() was called to generate a report.  This is*/
  58. /* used to determine how much of the information on the screen */
  59. /* we can reuse.                                               */
  60. int report_type;
  61. static int oldreport_type = -1;
  62.  
  63. /* oldmaxval 'remembers' the maximum range of the last time    */
  64. /* Report() was called.  If the maximum range has changed, we  */
  65. /* need to redraw just about everything.                       */
  66. static ULONG oldmaxval;
  67.  
  68. /* oldfullnames 'remembers' the old value of the 'fullnames'   */
  69. /* option.                                                     */
  70. static int oldfullnames = -1;
  71.  
  72. /* These two are the array index of the first function to   */
  73. /* appear on screen, and the number of entries that can fit */
  74. /* on the screen, respectively.                             */
  75. static int FirstOnScreen, NumOnScreen;
  76.  
  77. /* This variable is nonzero if the user is currently holding down the */
  78. /* mouse button on the scroll bar.  If this is the case, we don't     */
  79. /* update the scroll bar, since this would jerk the knob out from the */
  80. /* user's control.                                                    */
  81. static int bardown;
  82.  
  83. /* These are the IDCMP flags we use when we open the window. */
  84. #define IDCMPFLAGS (IDCMP_CLOSEWINDOW   | IDCMP_MENUPICK   | \
  85.                     IDCMP_REFRESHWINDOW | IDCMP_INTUITICKS | \
  86.                     IDCMP_GADGETDOWN    | IDCMP_GADGETUP   | \
  87.                     IDCMP_RAWKEY        | IDCMP_NEWSIZE)
  88.  
  89. /* Several #defines follow which define the edges of the drawing area */
  90. /* and its various subareas.                                          */
  91.  
  92. /* These four define the sizes of the margins on the various sides of */
  93. /* drawing area.  They use 'win', which points to the display window. */
  94. #define LEFT_MARGIN (win->BorderLeft+5)   
  95. #define RIGHT_MARGIN (win->BorderRight+5)
  96. #define TOP_MARGIN (win->BorderTop+5)
  97. #define BOTTOM_MARGIN (win->BorderBottom+5)
  98.  
  99. /* TEXT_LEFT is the leftmost edge of the function label area          */
  100. #define TEXT_LEFT LEFT_MARGIN
  101.  
  102. /* These four define the four boundaries of the histogram area        */
  103. /* They use the externs 'win', 'hist_bottom', and 'text_width', all   */
  104. /* which are defined later in this file.                              */
  105. #define HIST_LEFT (TEXT_LEFT+text_width+3)
  106. #define HIST_RIGHT (win->Width - RIGHT_MARGIN)
  107. #define HIST_BOTTOM hist_bottom
  108. #define HIST_TOP TOP_MARGIN
  109.  
  110. /* This one defines the width of the histogram area.  labellen is an  */
  111. /* extern which is the number of pixels to reserve for the histogram  */
  112. /* label.                                                             */
  113. #define HIST_WIDTH (HIST_RIGHT - HIST_LEFT - labellen)
  114.  
  115. /* MAX_TEXT_WIDTH is the biggest the text area will grow to.  MIN is  */
  116. /* the smallest it will ever be, and its initial size.                */
  117. #define MAX_TEXT_WIDTH 200
  118. #define MIN_TEXT_WIDTH  80
  119.  
  120. /* labellen is the size of the biggest histogram label.  We need to save */
  121. /* room on the right of the histogram for these labels.                  */
  122. ULONG labellen;
  123.  
  124. /* text_width is the current size, in pixels, of the text area (used to  */
  125. /* label the histogram.)  old_text_width stores the text_width value as  */
  126. /* of the previous call to Report(), so we can detect changes and force  */
  127. /* a redraw.                                                             */
  128. USHORT text_width = MIN_TEXT_WIDTH;
  129. USHORT old_text_width;
  130.  
  131. /* TICK_HEIGHT is the height (in pixels) of the tick marks on the axis.  */
  132. #define TICK_HEIGHT 8  // Needs to be divisible by 2
  133.  
  134. /* axis_top is the position of the top of the axis.  hist_bottom is the */
  135. /* bottom position of the histogram.  They are closely related: if we   */
  136. /* are in fact drawing an axis, they are 5 pixels apart.  If we are not,*/
  137. /* they are the same value.                                             */
  138. static int axis_top;
  139. static int hist_bottom;
  140.  
  141. /* The following #define computes x as a percentage of m, returning */
  142. /* the results as an integer and performing rounding.  Everything is*/
  143. /* done using integer math.                                         */
  144. #define PCTOF(x,m) ((m) == 0 ? 0 : (((x)*1000+5)/((m)*10)))
  145.  
  146. /* NOTE: Keep the #defines in this array up to date wrt the contents. */
  147. /*       They are used to update the checkmarks before creating the   */
  148. /*       menus, in InstallMenus() below.                               */
  149. static struct NewMenu mynewmenu[] =
  150.       {
  151.          {NM_TITLE, "Draw Histogram",   0 , 0, 0, 0,},
  152.          #define DRAW_TYPE_INDEX 1
  153.          { NM_ITEM, "Percent",          "1",  CHECKIT, ~1, 0,},
  154.          { NM_ITEM, "Time (excl.)" ,    "2", CHECKIT, ~2, 0,},
  155.          { NM_ITEM, "Time (incl.)" ,    "3", CHECKIT, ~4, 0,},
  156.          { NM_ITEM, "Count" ,           "4", CHECKIT, ~8, 0,},
  157.          { NM_ITEM, NM_BARLABEL,        0 , 0, 0, 0,},
  158.          { NM_ITEM, "Quit...",         "Q", 0, 0, 0,},
  159.          {NM_TITLE, "Options",          0 , 0, 0, 0,},
  160.          #define SORT_INDEX 8
  161.          { NM_ITEM, "Numeric Sort",    "N", CHECKIT, 2, 0,},
  162.          { NM_ITEM, "Alphabetic Sort", "A", CHECKIT, 1, 0,},
  163.          { NM_ITEM, NM_BARLABEL,        0 , 0, 0, 0,},
  164.          #define DRAW_AXIS_INDEX 11
  165.          { NM_ITEM, "Draw Axis",       "X", CHECKIT|MENUTOGGLE, 4, 0,},
  166.          #define FULL_NAMES_INDEX 12
  167.          { NM_ITEM, "Full Names",      "F", CHECKIT, 8, 0,},
  168.          {  NM_END, NULL,               0 , 0, 0, 0,}, 
  169.       };
  170.  
  171. static struct VBar       *VBar;      // Controls the scroll bar
  172. static struct Window     *win;       // Our display window
  173. static struct Menu       *menuStrip; // Our menus
  174. static struct TextAttr   myTextAttr; // TextAttr for the font
  175. static struct IntuiText  myIText;    // A convenient IntuiText struct
  176. static int baseline;                 // Baseline of the font we're using
  177.  
  178. #define YSIZE    myTextAttr.ta_YSize // Size of a single histogram line
  179.  
  180. /* externs from the SAS/C library.  _WBenchMsg is a quick'n'easy way  */
  181. /* to tell if we were invoked from WorkBench: it will be non-NULL if  */
  182. /* we were.   __stdiowin[] is the CON: specification for the stdio    */
  183. /* window, in case we need to give the user an error message.         */
  184. extern struct WBStartup *_WBenchMsg;
  185. extern char __stdiowin[];
  186.  
  187. /* InitReport() is an initialization routine.  It's called just after */
  188. /* arguments are parsed, but before anything else is done.            */
  189. int InitReport(void)
  190. {
  191.    BPTR fh;
  192.    struct DrawInfo *drawinfo;
  193.    APTR  visual_info;
  194.    struct Screen     *screen;    // Our screen
  195.    char *msg;
  196.  
  197.    /* Open window, attach menus and gadgets, then return. */
  198.    /* the struct Window * has to be an external variable  */
  199.  
  200.    if (screen = LockPubScreen(NULL)) 
  201.    {
  202.       if (drawinfo = GetScreenDrawInfo(screen))
  203.       {
  204.          myIText.FrontPen = drawinfo->dri_Pens[TEXTPEN];
  205.          myIText.BackPen  = drawinfo->dri_Pens[BACKGROUNDPEN];
  206.          myIText.DrawMode           = JAM2;
  207.          myIText.LeftEdge           = 0;
  208.          myIText.TopEdge            = 0;
  209.          myIText.ITextFont          = &myTextAttr;
  210.          myIText.NextText           = NULL;
  211.  
  212.          myTextAttr.ta_Name  = drawinfo->dri_Font->tf_Message.mn_Node.ln_Name;
  213.          myTextAttr.ta_YSize = drawinfo->dri_Font->tf_YSize;
  214.          if(myTextAttr.ta_YSize <= 0) myTextAttr.ta_YSize = 8;
  215.          myTextAttr.ta_Style = drawinfo->dri_Font->tf_Style;
  216.          myTextAttr.ta_Flags = drawinfo->dri_Font->tf_Flags;
  217.          baseline = drawinfo->dri_Font->tf_Baseline;
  218.  
  219.          FreeScreenDrawInfo(screen,drawinfo);
  220.       }
  221.    }
  222.    if (NULL != (win = OpenWindowTags(NULL,
  223.                            WA_Width,        500,    
  224.                            WA_Height,       400,    
  225.                            WA_MinWidth,     400,    
  226.                            WA_MinHeight,    150,
  227.                            WA_MaxWidth,     -1,    
  228.                            WA_MaxHeight,    -1,    
  229.                            WA_Activate,     TRUE,
  230.                            WA_CloseGadget,  TRUE,
  231.                            WA_Title,        "GUIPROF: Waiting", 
  232.                            WA_DragBar,      TRUE,
  233.                            WA_DepthGadget,  TRUE,
  234.                            WA_SizeGadget,   TRUE,
  235.                            WA_PubScreen,    screen,
  236.                            WA_SmartRefresh, TRUE,
  237.                            WA_IDCMP,        IDCMPFLAGS,
  238.                            TAG_END)))
  239.    {
  240.       /* Initialize the scrollbar and menus */
  241.       if(VBar = VBInit(win))
  242.       {
  243.          VBUpdate(VBar, VBMAXPOS, 0);
  244.          if(visual_info = GetVisualInfo(win->WScreen, TAG_END))
  245.          {
  246.             DoTitle(NULL);
  247.             InstallMenus(visual_info);
  248.             FreeVisualInfo(visual_info);
  249.             SetNums();
  250.             UnlockPubScreen(NULL,screen);
  251.             return 0;
  252.          }
  253.       }
  254.    }
  255.  
  256.    msg = "GUIPROF: Fatal error: can't initialize\n";
  257.  
  258. domsg:
  259.    if(screen) UnlockPubScreen(NULL,screen);
  260.    if((fh=Output()) == NULL)
  261.       fh = Open(__stdiowin, MODE_NEWFILE);
  262.    
  263.    if(fh)
  264.    {
  265.       #define MSG(a,b) Write(a,b,strlen(b))
  266.       MSG(fh, msg);
  267.       if(_WBenchMsg != NULL) Delay(200);
  268.       if(fh != Output()) Close(fh);
  269.    }
  270.    
  271.    return 1;
  272. }
  273.  
  274. /* Cleans up and closes down */
  275. void CleanupWindowStuff(void)
  276. {
  277.    if(win)
  278.    {
  279.       if(menuStrip)
  280.       {
  281.          /* Kill and free the menus */
  282.          ClearMenuStrip(win);
  283.          FreeMenus(menuStrip);
  284.          menuStrip = NULL;
  285.       }
  286.       /* Kill the scroll bar */
  287.       if(VBar) VBTerm(VBar);
  288.  
  289.       /* Close the window */
  290.       CloseWindow(win);
  291.       win = NULL;
  292.    }
  293. }
  294.  
  295. /* Scroll the display up 'count' elements */
  296. static void ScrollUp(int count)
  297. {
  298.    if(FirstOnScreen >= count) FirstOnScreen-=count;
  299.    else FirstOnScreen = 0;
  300.    if(!bardown) VBUpdate(VBar, VBSIZ(NumOnScreen), VBPOS(FirstOnScreen));
  301. }
  302.  
  303. /* Scroll the display down 'count' elements */
  304. static void ScrollDown(int count)
  305. {
  306.    FirstOnScreen += count;
  307.    if(FirstOnScreen+NumOnScreen > GPCur)
  308.       FirstOnScreen = GPCur - NumOnScreen;
  309.    if(!bardown) VBUpdate(VBar, VBSIZ(NumOnScreen), VBPOS(FirstOnScreen));
  310. }
  311.  
  312. /* Get and handle all pending Intuition messages */
  313. /* If the 'wait' parameter is nonzero, we wait for the CLOSE message */
  314. /* otherwise, we return as soon as we've done all pending messages.  */
  315. /* The 'now' parameter tells us the current moment's index so we can */
  316. /* compute time spent on the stack correctly; we just pass it through*/
  317. /* to Report().                                                      */
  318. /* Note that this routine is called from Report(), but it also calls */
  319. /* Report() at times.                                                */
  320. static void handle_window_events(int wait, sptime now)
  321. {
  322.    struct IntuiMessage *msg;
  323.    int done;
  324.    unsigned long pos;
  325.    UWORD menuNumber;
  326.    UWORD menuNum;
  327.    UWORD itemNum;
  328.    struct MenuItem *item;
  329.    static int eattick;
  330.  
  331.    done = FALSE;
  332.    while(!done)
  333.    {
  334.       while(!done && (msg=(struct IntuiMessage *)GetMsg(win->UserPort)))
  335.       {
  336.          switch (msg->Class)
  337.          {
  338.             case IDCMP_SIZEVERIFY:
  339.                RBUG(("IDCMP_SIZEVERIFY\n"))
  340.                break;
  341.  
  342.             case IDCMP_CLOSEWINDOW:
  343.                RBUG(("IDCMP_CLOSEWINDOW\n"))
  344.                /* They hit the close gadget.  Set 'broken' so we behave */
  345.                /* like they hit CTRL-C if the program is still running. */
  346.                done = TRUE;
  347.                broken = TRUE;
  348.                break;
  349.             
  350.             case IDCMP_RAWKEY:
  351.                #define UP_ARROW   0x4c
  352.                #define DOWN_ARROW 0x4d
  353.                #define PAGE_UP    0x3f
  354.                #define PAGE_DOWN  0x1f
  355.                RBUG(("IDCMP_RAWKEY KeyCode 0x%02.2x\n", msg->Code))
  356.                switch(msg->Code)
  357.                {
  358.                   case UP_ARROW:   ScrollUp(1);             break;
  359.                   case DOWN_ARROW: ScrollDown(1);           break;
  360.                   case PAGE_UP:    ScrollUp(NumOnScreen);   break;
  361.                   case PAGE_DOWN:  ScrollDown(NumOnScreen); break;
  362.                }
  363.                break;
  364.             
  365.          case IDCMP_GADGETDOWN:
  366.             if(VBSelected(VBar, VB_DOWN))
  367.             {
  368.                /* Down arrow on scroll bar */
  369.                RBUG(("IDCMP_GADGETDOWN VB_DOWN\n"))
  370.                ScrollDown(1);
  371.                /* We set 'eattick' so that the next IntuiTick will not */
  372.                /* result in the display changing if they continue to   */
  373.                /* hold the button down.                                */
  374.                eattick = 1;
  375.             }
  376.             else if(VBSelected(VBar, VB_UP))
  377.             { 
  378.                /* Up arrow on scroll bar */
  379.                RBUG(("IDCMP_GADGETDOWN VB_UP\n"))
  380.                ScrollUp(1);
  381.                /* We set 'eattick' so that the next IntuiTick will not */
  382.                /* result in the display changing if they continue to   */
  383.                /* hold the button down.                                */
  384.                eattick = 1;
  385.             }  
  386.             else 
  387.             {
  388.                if(VBSelected(VBar, VB_SLIDER))
  389.                {
  390.                   /* Diddling with scroll bar knob */
  391.                   RBUG(("IDCMP_GADGETDOWN scroll bar\n"))
  392.                   bardown = 1;
  393.                }
  394.                else if(GADP(msg->IAddress)->GadgetID == VB_SLIDER)
  395.                {
  396.                   /* Click on scroll bar box */
  397.                   RBUG(("IDCMP_GADGETDOWN scroll bar box\n"))
  398.                }
  399.                else
  400.                   break;
  401.  
  402.                /* Update our position wrt the scroll bar */
  403.                pos = VBRead(VBar);
  404.                FirstOnScreen = (int)((GPCur - NumOnScreen)*pos / VBMAXPOS);
  405.                assert(FirstOnScreen < GPCur);
  406.                assert(FirstOnScreen >= 0);
  407.             }
  408.             break;
  409.  
  410.          case IDCMP_GADGETUP:
  411.             /* They let the bar up; resume setting it when things change */
  412.             bardown = 0;
  413.             break;
  414.  
  415.          case IDCMP_INTUITICKS:
  416.             if(eattick) 
  417.             {
  418.                /* They pushed down on a scroll bar up or down gadget */
  419.                /* recently; don't update wrt the scroll bar in this  */
  420.                /* case since we just did it.                         */
  421.                eattick = 0; 
  422.                break;
  423.             }
  424.             //if up arrow gadget is selected and held down then scroll text up
  425.             //in small increments
  426.             if(VBSelected(VBar,VB_UP))
  427.             {
  428.                ScrollUp(1);
  429.             }
  430.             //else if down arrow gadget is selected and held down then scroll 
  431.             //text down in small increments 
  432.             else if(VBSelected(VBar,VB_DOWN))
  433.             {
  434.                ScrollDown(1);
  435.             }
  436.             else if(VBSelected(VBar, VB_SLIDER))
  437.             {
  438.                /* If the slider is selected, update the display wrt it */
  439.                pos = VBRead(VBar);
  440.                FirstOnScreen = (int)((GPCur - NumOnScreen)*pos / VBMAXPOS);
  441.                assert(FirstOnScreen < GPCur);
  442.                assert(FirstOnScreen >= 0);
  443.             }
  444.             break;
  445.  
  446.          case IDCMP_MENUPICK:
  447.             RBUG(("IDCMP_MENUPICK\n"))
  448.             menuNumber = msg->Code;
  449.             while ((menuNumber != MENUNULL) && (!done))
  450.             {
  451.                item = ItemAddress(menuStrip, menuNumber);
  452.                
  453.                menuNum = MENUNUM(menuNumber);
  454.                itemNum = ITEMNUM(menuNumber);
  455.                //subNum  = SUBNUM(menuNumber);
  456.  
  457.                /* See "guiprofpriv.h" for menu item #defines */
  458.                switch(menuNum)
  459.                {
  460.                   case MENU_HIST:
  461.                      if(itemNum == HIST_QUIT)
  462.                         done = TRUE;
  463.                      else if(report_type != itemNum)
  464.                      {
  465.                         report_type = itemNum;
  466.                         KillCaches();
  467.                         Report(now);
  468.                      }
  469.                      break;
  470.  
  471.                   case MENU_OPT:
  472.                      if(itemNum == OPT_AXIS)
  473.                      {
  474.                         axis = !axis;
  475.                         SetNums();
  476.                         KillCaches();
  477.                         Report(now);
  478.                      }
  479.                      else if(itemNum == OPT_FULLNAME)
  480.                      {
  481.                         fullnames = !fullnames;
  482.                         SetNums();
  483.                         KillCaches();
  484.                         Report(now);
  485.                      }
  486.                      else if(itemNum+1 != sortby)
  487.                      {
  488.                         sortby = itemNum+1;
  489.                         KillCaches();
  490.                         Report(now);
  491.                      }
  492.                      break;
  493.                }
  494.                menuNumber = item->NextSelect;
  495.             }
  496.             break;
  497.          
  498.          case IDCMP_NEWSIZE:
  499.             RBUG(("IDCMP_NEWSIZE\n"))
  500.             SetNums();
  501.             /* Fall through */
  502.  
  503.          case IDCMP_REFRESHWINDOW:
  504.             RBUG(("IDCMP_REFRESHWINDOW\n"))
  505.             KillCaches();  /* Forces an erase */
  506.             Report(now);
  507.             break;
  508.  
  509.          default:
  510.             RBUG(("unknown IDCMP message\n"))
  511.             assert(1);
  512.          }
  513.       ReplyMsg((struct Message *)msg);
  514.       }
  515.       if(wait && !done && !autoexit)
  516.       {
  517.          Report(now);
  518.          Wait(1L << win->UserPort->mp_SigBit);
  519.       }
  520.       else done = TRUE;
  521.    }
  522. }
  523.  
  524. void _STDReport(void)
  525. {
  526.    /* Handle window events until the user selects QUIT   */
  527.    /* then clean up, close the window and return         */
  528.  
  529.    if(win)
  530.    {
  531.       DoTitle("Complete");
  532.       handle_window_events(1,0);
  533.    }
  534.  
  535.    CleanupWindowStuff();
  536. }
  537.  
  538. /* The following routines are called from qsort() to sort based */
  539. /* on different criteria                                        */
  540.  
  541. /* Sort by ETime */
  542. static int GPByETime(struct GPInfo **a, struct GPInfo **b)
  543. {
  544.    sptime atime = (*a)->time + (*a)->stkval;
  545.    sptime btime = (*b)->time + (*b)->stkval;
  546.    return(atime < btime ? 1 : atime > btime ?  -1 : 0);
  547. }
  548.  
  549. /* Sort by ITime */
  550. static int GPByITime(struct GPInfo **a, struct GPInfo **b)
  551. {
  552.    sptime atottime = (*a)->tottime + (*a)->stkval;
  553.    sptime btottime = (*b)->tottime + (*b)->stkval;
  554.    return(atottime < btottime ? 1 : atottime > btottime ?  -1 : 0);
  555. }
  556.  
  557. /* Sort by Count */
  558. static int GPByCount(struct GPInfo **a, struct GPInfo **b)
  559. {
  560.    int acount = (*a)->count + (*a)->stkval;
  561.    int bcount = (*b)->count + (*b)->stkval;
  562.    return(acount < bcount ? 1 : acount > bcount ?  -1 : 0);
  563. }
  564.  
  565. /* Sort alphabetically by function name (ignore case) */
  566. static int GPByAlpha(struct GPInfo **a, struct GPInfo **b)
  567. {
  568.       int i;
  569.       char *ap = GPINAME(*a);
  570.       char *bp = GPINAME(*b);
  571.       int ac, bc;
  572.       i = 0;
  573.  
  574.       do
  575.       {
  576.          ac = toupper(ap[i]);
  577.          bc = toupper(bp[i]);
  578.          i++;
  579.       }
  580.       while(ac && ac == bc);
  581.  
  582.       return(ac < bc ? -1 : ac > bc ? 1 : 0);
  583. }
  584.  
  585. /* Given a 'long' name in the form "\module\func\line", return */
  586. /* the function name.                                          */
  587. char *FuncName(char *p)
  588. {
  589.    static char tmpname[256];
  590.    char *q;
  591.    int j;
  592.  
  593.    assert(p != NULL);
  594.  
  595.    q = strchr(p+1, '\\');
  596.    if(*p != '\\' || q == NULL)
  597.    {
  598.       /* Badly formed name string */
  599.       strncpy(tmpname, p, sizeof(tmpname));
  600.       tmpname[sizeof(tmpname)-1] = 0;
  601.    }
  602.    else
  603.    {
  604.       p = q+1;
  605.       for(j=0; j<sizeof(tmpname)-1 && p[j] != '\\' ; j++)
  606.          tmpname[j] = p[j];
  607.       tmpname[j] = '\0';
  608.    }
  609.    return tmpname;
  610. }
  611.  
  612. /* Return the length of the specified name in pixels */
  613. USHORT GetLength(char *name)
  614. {
  615.    myIText.IText = name;
  616.    return (USHORT)IntuiTextLength(&myIText);
  617. }
  618.  
  619. /* Truncate the specified name to whatever will fit in the text area. */
  620. /* Return the length it's been truncated to in 'len'.                 */
  621. USHORT Truncate(char *name, USHORT *len)
  622. {
  623.    int namelen = strlen(name);
  624.    USHORT newlen;
  625.  
  626.    while(namelen>0 && (newlen=GetLength(name)) > MAX_TEXT_WIDTH)
  627.       name[--namelen] = 0;
  628.  
  629.    if(*len < newlen) *len = newlen;
  630.  
  631.    return (USHORT)namelen;
  632. }
  633.  
  634. /* 'scale' is the histogram scale - i.e. how many units each pixel */
  635. /* represents.                                                     */
  636. static ULONG scale;
  637.  
  638. /* KillCaches resets the various cached values so we can be assured    */
  639. /* that EVERYTHING will be redrawn the next time Report() gets called. */
  640. static void KillCaches(void)
  641. {
  642.    scale = 0xffffffff;
  643.    oldmaxval = 0xffffffff;
  644.    oldreport_type = 0xffffffff;
  645.    old_text_width = 0xffff;
  646.    oldfullnames = -1;
  647. }
  648.  
  649. /* SetNums calculates the axis_top, hist_bottom, and NumOnScreen externs.*/
  650. static void SetNums(void)
  651. {
  652.    if(axis)
  653.    {
  654.       axis_top = win->Height - BOTTOM_MARGIN - YSIZE - TICK_HEIGHT;
  655.       hist_bottom = axis_top - 5;
  656.    }
  657.    else
  658.    {
  659.       axis_top = hist_bottom = win->Height - BOTTOM_MARGIN;
  660.    }
  661.    NumOnScreen = (HIST_BOTTOM-HIST_TOP)/YSIZE;
  662.    if(NumOnScreen > GPCur) NumOnScreen = GPCur;
  663. }
  664.  
  665. /* CalcRange calculates the histogram range */
  666. /* 'maxval' is the maximum data value that must be graphed */
  667. /* 'minmax' and 'maxmax' are the minimum and maximum acceptable values */
  668. /* for the histogram range.  (maxmax is primarily used to limit the    */
  669. /* PERCENTAGE histogram to no more than 100%.                          */
  670. static ULONG CalcRange(ULONG maxval, int minmax, int maxmax)
  671. {
  672.    int len;
  673.    char buf[32];
  674.  
  675.    /* First check to see if we can reuse the current scale  */
  676.    /* We can do this if the new maxval is less than the     */
  677.    /* current scale, and it's not "too small".  We consider */
  678.    /* anything less than 3/5 of the current maximum to be   */
  679.    /* too small.                                            */
  680.    if(scale != 0xffffffff && 
  681.       maxval<=scale && 5*maxval/3 >= scale) return scale;
  682.  
  683.    /* The old scale won't work.  Pick a new one that allows */
  684.    /* room for growth.                                      */
  685.    scale = 4*maxval/3;
  686.  
  687.    /* Make sure our choice is within any specified limits */
  688.    if(maxmax>0 && scale > maxmax) scale = maxmax;
  689.    if(minmax>0 && scale < minmax) scale = minmax;
  690.  
  691.    /* Double-check to make sure the scale includes the values */
  692.    if(scale < maxval) scale = maxval;
  693.  
  694.    /* Reduce the range as needed to get the labels in */
  695.    /* labellen is used by the HIST_WIDTH macro        */
  696.    sprintf(buf, " %d", scale);
  697.    myIText.IText = buf;
  698.    if((len=IntuiTextLength(&myIText)) > labellen)
  699.    {
  700.       labellen = len;
  701.       /* Force a redraw */
  702.       oldreport_type = -1;
  703.    }
  704.    
  705.    return scale;
  706. }
  707.  
  708. /* Draw the axis, if requested */
  709. static void DoAxis(ULONG scale)
  710. {
  711.    char buf[32];
  712.    int i, x, textlen;
  713.  
  714.    if(!axis) return;  // Axis option not on
  715.  
  716.    /* Erase the current axis */
  717.    SetAPen(win->RPort, 0);
  718.    RectFill(win->RPort, win->BorderLeft, axis_top, 
  719.                         win->Width-win->BorderRight-1, 
  720.                         win->Height-win->BorderBottom-1);
  721.    
  722.    /* Draw the horizontal axis line */
  723.    SetAPen(win->RPort, 1);
  724.    Move(win->RPort, HIST_LEFT, axis_top+TICK_HEIGHT/2);
  725.    Draw(win->RPort, HIST_LEFT+HIST_WIDTH, axis_top+TICK_HEIGHT/2);
  726.  
  727.    /* Draw the tick marks */
  728.    for(i=0; i<5; i++)
  729.    {
  730.       x = HIST_LEFT + HIST_WIDTH*i/4;
  731.       Move(win->RPort, x, axis_top);
  732.       Draw(win->RPort, x, axis_top+TICK_HEIGHT);
  733.       sprintf(buf, "%d", scale*i/4);
  734.       myIText.IText = buf;
  735.       textlen = IntuiTextLength(&myIText);
  736.       PrintIText(win->RPort, &myIText, x-textlen/2, 
  737.                  axis_top+TICK_HEIGHT+YSIZE-baseline);
  738.    }
  739. }
  740.  
  741. /* Update the title bar. The title bar is of the form */
  742. /* "GUIPROF: <status>: Report by <reptype>"             */
  743. /* If specified, our parameter is the new status.     */
  744. void DoTitle(char *new)
  745. {   
  746.    static char title[80];
  747.    static char *graphtypes[] =
  748.    {
  749.      "Percent", 
  750.      "Time (No subroutines)",
  751.      "Time (With subroutines)",
  752.      "Count", 
  753.    };
  754.    char new_title[sizeof(title)];
  755.    static char *progstatus = "Waiting for program";
  756.  
  757.    if(new) progstatus = new;
  758.  
  759.    sprintf(new_title, "GUIPROF: %s: Report by %s", progstatus, 
  760.            graphtypes[report_type]);
  761.  
  762.    /* Check to see if this is different from the previous title */
  763.    /* before updating                                           */
  764.    if(strcmp(new_title, title))
  765.    {
  766.       strcpy(title, new_title);
  767.       SetWindowTitles(win, title, NULL);
  768.    }
  769. }
  770.  
  771. /* This is the big enchilada.  Produce the report. */
  772. /* Our parameter, 'now', is the timestamp of the latest event      */
  773. /* to come in.  This allows us to factor in time spent in routines */
  774. /* that haven't returned yet.                                      */
  775. void Report(sptime now)
  776. {
  777.    typedef int (*sortfunc_p)(void const *, void const *);
  778.    static sortfunc_p sortfuncs[] =
  779.    {
  780.       (sortfunc_p)GPByETime,
  781.       (sortfunc_p)GPByETime,
  782.       (sortfunc_p)GPByITime,
  783.       (sortfunc_p)GPByCount,
  784.    };
  785.    static int already_called;
  786.  
  787.    int ypos;
  788.    int i, indx, doname;
  789.  
  790.    sptime time, tottime, sumtime, cumtime;
  791.    int count;
  792.    ULONG maxval, newmaxval;
  793.    
  794.    int LastOnScreen, histlen;
  795.    ULONG histval;
  796.    
  797.    sptime elapsed, subelapsed;
  798.    static struct GPInfo **rptGPInfo;
  799.    static int oldgpmax;
  800.    struct GPInfo *gpi;
  801.    int freebies = 1;
  802.    char buf[20];
  803.  
  804.    /* If we haven't already been called once, handle window IDCMP */
  805.    /* events now.  Eventually this will return to us and we'll    */
  806.    /* continue with the report.                                   */
  807.    if(already_called == 0)
  808.    {
  809.       already_called = 1;
  810.       handle_window_events(0, now);
  811.       already_called = 0;
  812.    }
  813.  
  814.    /* Update the window titles, in case the report type changed */
  815.    DoTitle(NULL);
  816.    
  817.    if(now != 0)
  818.    {
  819.       /* Accumulate data for functions that are still on the stack   */
  820.       /* Note that if there are functions on the stack that have not */
  821.       /* returned yet, they will be added to the list by FindGPI.    */
  822.       /* Since FindGPI expects the list to be sorted by id, do not   */
  823.       /* move this code after the qsort() call below.                */ 
  824.       subelapsed = 0;
  825.       for(i=spcur-1; i>=0; i--)
  826.       {
  827.          assert(spdat[i].clk <= now);
  828.          elapsed = now - spdat[i].clk;
  829.          if(spdat[i].id && spdat[i].id != nullid)
  830.          {
  831.             gpi = FindGPI(&GPInfo, spdat[i].id, &GPCur, &GPMax);
  832.             assert(gpi->name == (char *)(gpi+1));
  833.             assert(gpi->fullname[0] == '\\');
  834.             assert(elapsed >= spdat[i].subrs + subelapsed);
  835.             if(report_type == RPT_PCT || report_type == RPT_ETIME)
  836.                gpi->stkval += (elapsed - spdat[i].subrs - subelapsed);
  837.             else if(report_type == RPT_ITIME)
  838.                gpi->stkval += elapsed;
  839.             else
  840.                gpi->stkval++;
  841.          }
  842.          subelapsed = elapsed;
  843.       }
  844.    }
  845.  
  846.    if(!GPCur) return;  // Nothing to display
  847.  
  848.    SetNums();
  849.  
  850.    /* Set the scroll bar to reflect our current status unless it's being */
  851.    /* diddled with right now.  If so, 'bardown' will be on.              */
  852.    if(!bardown) VBUpdate(VBar, VBSIZ(NumOnScreen), VBPOS(FirstOnScreen));
  853.  
  854.    /* We need our own private copy of the GPInfo array since we will */
  855.    /* be sorting it.                                                 */
  856.    if(GPMax != oldgpmax)
  857.    {
  858.       rptGPInfo = realloc(rptGPInfo, GPMax*sizeof(struct GPInfo *));
  859.       if(!rptGPInfo)
  860.       {
  861.          fprintf(stderr, "GUIPROF: Out of memory\n");
  862.          return;
  863.       }
  864.       oldgpmax = GPMax;
  865.    }
  866.    memcpy(rptGPInfo, GPInfo, GPCur*sizeof(struct GPInfo *));
  867.  
  868.    /* Sort the array */
  869.    qsort(rptGPInfo, GPCur, 
  870.          sizeof(struct GPInfo *), 
  871.          sortby == OPT_SORTNUM ? sortfuncs[report_type]
  872.                                : (sortfunc_p)GPByAlpha);
  873.  
  874.    /* Calculate totals */
  875.    sumtime = cumtime = maxval = 0;
  876.    for(i=0; i<GPCur; i++)
  877.    {
  878.       gpi = rptGPInfo[i];
  879.  
  880.       if(gpi->rptnamelen == 0 || oldfullnames != fullnames)
  881.          gpi->rptnamelen = Truncate(GPINAME(gpi), &text_width);
  882.  
  883.       assert(gpi->name == (char *)(gpi+1));
  884.  
  885.       switch(report_type)
  886.       {
  887.          case RPT_PCT:
  888.          case RPT_ETIME:
  889.             time = gpi->time + gpi->stkval;
  890.             sumtime += time;
  891.             if(time > maxval) maxval = time;
  892.             break;
  893.  
  894.          case RPT_CNT:
  895.             count = gpi->count + gpi->stkval;
  896.             if(count > maxval) maxval = count;
  897.             break;
  898.  
  899.          case RPT_ITIME:
  900.             tottime = gpi->tottime + gpi->stkval;
  901.             if(tottime > maxval) maxval = tottime;
  902.             break;
  903.       }
  904.    }
  905.    oldfullnames = fullnames;
  906.  
  907.    // Compute the maximum percentage if that's the report type we're doing
  908.    if(report_type == RPT_PCT)
  909.       maxval = PCTOF(maxval, sumtime);
  910.  
  911.    // Set the SIZEVERIFY flag on the window so it won't be resized while 
  912.    // we are working on it.
  913.    ModifyIDCMP(win, IDCMPFLAGS|IDCMP_SIZEVERIFY);
  914.  
  915.    // Rescale if necessary
  916.    SetNums();
  917.    newmaxval = CalcRange(maxval, 0, report_type == RPT_PCT ? 100 : 0);
  918.  
  919.    /* Check to see how much we can get away without drawing */
  920.    if(newmaxval != oldmaxval || 
  921.       report_type != oldreport_type ||
  922.       text_width != old_text_width)
  923.    {
  924.       /* Sorry, Charlie - have to redraw it all */
  925.       freebies = 0;
  926.       oldmaxval = newmaxval;
  927.       oldreport_type = report_type;
  928.       old_text_width = text_width;
  929.       SetAPen(win->RPort, 0);
  930.       RectFill(win->RPort, 
  931.                win->BorderLeft,
  932.                win->BorderTop,
  933.                win->Width - win->BorderRight-1,
  934.                win->Height - win->BorderBottom-1);
  935.       DoAxis(scale);
  936.    }
  937.    maxval = newmaxval;
  938.  
  939.    if(axis_top > win->Height - BOTTOM_MARGIN)
  940.    {
  941.       /* Somebody resized the window smaller on us */
  942.       /* Give up and let the next update do it.    */
  943.       ModifyIDCMP(win, IDCMPFLAGS);
  944.       return;
  945.    }
  946.    assert(axis_top > 0);
  947.    assert(hist_bottom <= axis_top);
  948.  
  949.    // Figure out the index of the first and last elements on screen
  950.    if(NumOnScreen > GPCur - FirstOnScreen)
  951.    {
  952.       FirstOnScreen = GPCur - NumOnScreen;
  953.       if(FirstOnScreen < 0) FirstOnScreen = 0;
  954.       LastOnScreen = GPCur;
  955.       if(!bardown) VBUpdate(VBar, VBSIZ(NumOnScreen), VBPOS(FirstOnScreen));
  956.    }
  957.    else
  958.       LastOnScreen = NumOnScreen + FirstOnScreen;
  959.  
  960.    assert(LastOnScreen >= 0);
  961.    assert(LastOnScreen <= GPCur);
  962.    assert(FirstOnScreen >= 0);
  963.    assert(FirstOnScreen <= LastOnScreen);
  964.  
  965.    /* Reset rptindx for those that aren't on screen */
  966.    for(i=0; i<FirstOnScreen; i++)
  967.       rptGPInfo[i]->rptindx = -1;
  968.  
  969.    for(i=LastOnScreen; i<GPCur; i++)
  970.       rptGPInfo[i]->rptindx = -1;
  971.  
  972.    ypos = HIST_TOP;
  973.    indx = 1;
  974.    
  975.    for(i=FirstOnScreen; i<LastOnScreen; i++, ypos += YSIZE)
  976.    {
  977.       gpi = rptGPInfo[i];
  978.  
  979.       /* Calculate the length of the new rectangle */
  980.       if(maxval == 0) 
  981.          histlen = 0;
  982.       else
  983.       {
  984.          if(report_type == RPT_ITIME)
  985.             histval = gpi->tottime + gpi->stkval;
  986.          else if(report_type == RPT_CNT)
  987.             histval = gpi->count + gpi->stkval;
  988.          else
  989.          {
  990.             histval = gpi->time + gpi->stkval;
  991.             if(report_type == RPT_PCT) 
  992.                histval = PCTOF(histval, sumtime);
  993.          }
  994.          gpi->stkval = 0;
  995.          histlen = (histval*HIST_WIDTH)/maxval;
  996.       }
  997.       assert(histlen>=0 && histlen<=HIST_WIDTH);
  998.  
  999.       if(histval && !histlen) histlen = 1;
  1000.  
  1001.       /* At this point, histval is the numerical value of the current bar */
  1002.       /* and histlen is the length in pixels of the current bar.          */
  1003.  
  1004.       if(gpi->rptindx == indx)
  1005.       {
  1006.          /* This element occurred at this index last time through */
  1007.          /* Check to see if we can use any of the previous stuff  */
  1008.          if(freebies && 
  1009.             gpi->histval == histval && 
  1010.             gpi->histlen == histlen)
  1011.          {
  1012.             indx++;
  1013.             continue;  // Everything's the same
  1014.          }
  1015.          // If we're accepting freebies, then we know we can leave the
  1016.          // name as is.  Set 'doname' appropriately.
  1017.          doname = (freebies ? 0 : 1);
  1018.       }
  1019.       else  // Not the same index as last time, must redo the name
  1020.          doname = 1;
  1021.  
  1022.       /* Check to see if they've resized the window on us */
  1023.       if(ypos + YSIZE > win->Height - BOTTOM_MARGIN) break;
  1024.  
  1025.       if(doname)
  1026.       {
  1027.          /* Erase the old function name and bar */
  1028.          SetAPen(win->RPort, 0);
  1029.          RectFill(win->RPort, LEFT_MARGIN, ypos,
  1030.                   win->Width-RIGHT_MARGIN, ypos+YSIZE-1);
  1031.  
  1032.          /* Draw the function name */
  1033.          SetAPen(win->RPort, 1);
  1034.          myIText.IText = GPINAME(gpi);
  1035.          myIText.IText[gpi->rptnamelen] = 0;
  1036.          PrintIText(win->RPort, &myIText, TEXT_LEFT, ypos);
  1037.  
  1038.          /* Draw the bar */
  1039.          if(histlen > 0)
  1040.          {
  1041.             SetAPen(win->RPort, 3);
  1042.             RectFill(win->RPort, HIST_LEFT, ypos,
  1043.                      HIST_LEFT+histlen, ypos+YSIZE-2);
  1044.          }
  1045.          /* The label annotation is done below */
  1046.       }
  1047.       else if(freebies)
  1048.       {
  1049.          if(gpi->histlen > histlen)
  1050.          {
  1051.             /* New bar is smaller than the old bar - can happen with   */
  1052.             /* percentage reports.                                     */
  1053.             /* Erase the portion of the old bar that no longer applies */
  1054.             SetAPen(win->RPort, 0);
  1055.             RectFill(win->RPort, HIST_LEFT+histlen, ypos,
  1056.                      HIST_LEFT+HIST_WIDTH+labellen, ypos+YSIZE-1);
  1057.          }
  1058.          else if(gpi->histlen < histlen)
  1059.          {
  1060.             /* New bar is bigger than the old bar. */
  1061.             /* Draw the new portion of the bar     */
  1062.             if(histlen > 0)
  1063.             {
  1064.                SetAPen(win->RPort, 3);
  1065.                RectFill(win->RPort, HIST_LEFT+gpi->histlen, ypos,
  1066.                         HIST_LEFT+histlen, ypos+YSIZE-2);
  1067.             }
  1068.          }
  1069.       }
  1070.       else
  1071.       {
  1072.          /* Erase the old bar */
  1073.          SetAPen(win->RPort, 0);
  1074.          RectFill(win->RPort, HIST_LEFT, ypos, 
  1075.                   win->Width-RIGHT_MARGIN, ypos+YSIZE-1);
  1076.  
  1077.          /* Draw the new bar */
  1078.          if(histlen > 0)
  1079.          {
  1080.             SetAPen(win->RPort, 3);
  1081.             RectFill(win->RPort, HIST_LEFT, ypos,
  1082.                      HIST_LEFT+histlen, ypos+YSIZE-2);
  1083.          }
  1084.       }
  1085.  
  1086.       /* Annotate the bar with the value */
  1087.       SetAPen(win->RPort, 1);
  1088.       sprintf(buf, " %d", histval);
  1089.       myIText.IText = buf;
  1090.       PrintIText(win->RPort, &myIText, 
  1091.                  HIST_LEFT+histlen+1, ypos);
  1092.  
  1093.       gpi->rptindx = indx++;
  1094.       gpi->histval = histval;
  1095.       gpi->histlen = histlen;
  1096.    }
  1097.    /* Remove the SIZEVERIFY flag */
  1098.    ModifyIDCMP(win, IDCMPFLAGS);
  1099.  
  1100. }
  1101.  
  1102. static void InstallMenus(struct VisualInfo *vi)
  1103. {
  1104.    /* Make the menus reflect the user's command-line options */
  1105.    /* This is VERY dependant on the menu layouts!            */
  1106.  
  1107.    /* Check the selected report type */
  1108.    mynewmenu[report_type+DRAW_TYPE_INDEX].nm_Flags |= CHECKED;
  1109.  
  1110.    /* Check the AXIS option if selected */
  1111.    if(axis) mynewmenu[DRAW_AXIS_INDEX].nm_Flags |= CHECKED;
  1112.  
  1113.    /* Check the FULLNAMES option if selected */
  1114.    if(fullnames) mynewmenu[FULL_NAMES_INDEX].nm_Flags |= CHECKED;
  1115.  
  1116.    mynewmenu[SORT_INDEX+sortby-1].nm_Flags |= CHECKED;
  1117.  
  1118.    if (NULL != (menuStrip = CreateMenus(mynewmenu, TAG_END)))
  1119.    {
  1120.       LayoutMenus(menuStrip, vi, TAG_END);
  1121.       SetMenuStrip(win, menuStrip);
  1122.    }
  1123.  
  1124. }
  1125.